package medusa

import (
	"errors"
	"fmt"
	"net"
)

//////////////////////////////////////////////////////////////////////////
//Listener
//////////////////////////////////////////////////////////////////////////
type Listener interface {
	GetName() string
	SendPacket(acc_uuid int, pack *Packet)
	Broadcast(pack *Packet, except_uuid int)

	get_log() Log
	get_rcvbuf_size() int
	get_sndbuf_size() int
	get_sndchan_num() int
	get_max_idle() int64

	CallMethod(method_name string, args ...interface{}) (interface{}, error)
}

//////////////////////////////////////////////////////////////////////////
//TcpListener
//////////////////////////////////////////////////////////////////////////

type TcpListener struct {
	HasLog
	StateCounter
	Tasklet

	name string
	host string
	port int
	fd   *net.TCPListener

	max_conn           int
	accept_thread_num  int
	local_max_idle_sec int64
	recv_buf_size      int
	send_buf_size      int
	send_chan_num      int

	clients           map[int]Acceptor
	chan_free_acc     chan Acceptor
	client_uuid_count int

	ecb_gen_uuid    func() int
	ecb_create_conn func(uuid int) Acceptor
	ecb_conn_made   func(acc Acceptor)
	ecb_conn_lose   func(acc Acceptor)
}

func NewListener(name string, max_conn int, rcv_buf int, snd_buf int, sndchan int, max_idle_sec int64,
	log Log,
	ecb_gen_uuid func() int,
	ecb_create_conn func(uuid int) Acceptor,
	ecb_conn_made func(acc Acceptor),
	ecb_conn_lose func(acc Acceptor)) *TcpListener {

	listener := new(TcpListener)
	listener.Constructor(name, max_conn, rcv_buf, snd_buf, sndchan, max_idle_sec, log,
		ecb_gen_uuid, ecb_create_conn, ecb_conn_made, ecb_conn_lose)

	return listener
}

func (self *TcpListener) Constructor(name string, max_conn int, rcv_buf int, snd_buf int, sndchan int,
	max_idle_sec int64,
	log Log,
	ecb_gen_uuid func() int,
	ecb_create_conn func(uuid int) Acceptor,
	ecb_conn_made func(acc Acceptor),
	ecb_conn_lose func(acc Acceptor)) {

	self.max_conn = max_conn
	self.accept_thread_num = max_conn / 100
	if self.accept_thread_num <= 0 {
		self.accept_thread_num = 1
	}
	self.recv_buf_size = rcv_buf
	self.send_buf_size = snd_buf
	self.send_chan_num = sndchan
	self.local_max_idle_sec = max_idle_sec
	self.ecb_gen_uuid = ecb_gen_uuid
	self.ecb_create_conn = ecb_create_conn
	self.ecb_conn_made = ecb_conn_made
	self.ecb_conn_lose = ecb_conn_lose

	self.clients = make(map[int]Acceptor)

	self.chan_free_acc = make(chan Acceptor, self.max_conn+self.accept_thread_num)
	self.client_uuid_count = 0

	self.log = log
	self.name = name
	self_name := fmt.Sprintf("%v-Listener", name)
	self.Tasklet.Constructor(self_name)
	self.HasLog.Constructor(self_name, log)

	if self.ecb_gen_uuid == nil {
		self.ecb_gen_uuid = func() int { return self.in_gen_uuid_default() }
	}

	if self.ecb_create_conn == nil {
		self.ecb_create_conn = func(uuid int) Acceptor {

			return NewAcceptor(self, self.in_gen_uuid_default(),
				//ecb_idle        
				func() {
					self.Warning("OnIdle")
				},

				//ecb_pre_sent    
				func(pack *Packet) {

				},

				//ecb_conn_lose
				func() {
					self.Warning("OnConnLose")
				},

				//ecb_unknow_packet_received 
				func(pack *Packet) {
					self.Warning("OnUnknowData")
				})
		}

	}

	//method
	self.AddMethod("listen-conn-made", func(args ...interface{}) (result interface{}, err error) {
		id := args[0].(int)
        self.Add(1)
		acc := args[1].(Acceptor)
		self.Success("----> accept thread:%d make conn '%v' success.", id, acc.GetName())
		self.clients[acc.GetUUID()] = acc
		
		if acc != nil {
			self.ecb_conn_made(acc)
		}
		return
	})

	self.AddMethod("listen-conn-lose", func(args ...interface{}) (result interface{}, err error) {
		acc := args[0].(Acceptor)
		self.Done()
		if acc != nil {
			delete(self.clients, acc.GetUUID())
			self.ecb_conn_lose(acc)
			self.chan_free_acc <- acc //release acceptor to pool
		}
		return
	})

	self.AddMethod("listen-close-acc", func(args ...interface{}) (result interface{}, err error) {
		acc_uuid := args[0].(int)
		acc := self.clients[acc_uuid]
		if acc != nil {
			acc.Close()
		}
		return
	})

	self.AddMethod("listen-send-data", func(args ...interface{}) (result interface{}, err error) {
		acc_uuid := args[0].(int)
		pack := args[1].(*Packet)

		acc := self.clients[acc_uuid]
		if acc != nil {
			acc.SendPacket(pack)
		}
		return
	})

	self.AddMethod("listen-broadcast", func(args ...interface{}) (result interface{}, err error) {
		pack := args[0].(*Packet)
		except_uuid := args[1].(int)

		for _, acc := range self.clients {
			if acc.GetUUID() != except_uuid {
				acc.SendPacket(pack)
			}
		}
		return
	})

	self.AddMethod("listen-close-all-conn", func(args ...interface{}) (result interface{}, err error) {

		for _, acc := range self.clients {
			if acc != nil {
				acc.Close()
			}
		}
		return
	})

	//create acceptor pool
	for i := 0; i < self.max_conn+self.accept_thread_num; i++ {
		acc := self.ecb_create_conn(self.ecb_gen_uuid())
		self.chan_free_acc <- acc
	} //end :for

}

///////////////////////////////////////////////////////////////////////////////////////
//抛出事件
func (self *TcpListener) in_gen_uuid_default() int {
	self.client_uuid_count = self.client_uuid_count + 1
	return self.client_uuid_count
}

func (self *TcpListener) SendPacket(acc_uuid int, pack *Packet) {
	self.CallMethod("listen-send-data", acc_uuid, pack)
}

func (self *TcpListener) Broadcast(pack *Packet, except_uuid int) {
	self.CallMethod("listen-broadcast", pack, except_uuid)
}

func (self *TcpListener) CloseAcceptor(acc_uuid int) {
	self.CallMethod("listen-close-acc", acc_uuid)
}

func (self *TcpListener) CloseAllAcceptor() {
	self.CallMethod("listen-close-all-conn")
}

///////////////////////////////////////////////////////////////////////////////////////
//其他

func (self *TcpListener) Run(host string, port int) (err error) {

	if !self.SwicthWorking() { //already running!
		errinfo := fmt.Sprintf("(%v:%v):listening already opened", host, port)
		err = errors.New(errinfo)
		self.Error(errinfo)
		return
	}
	self.host = host
	self.port = port
	//str_listen := fmt.Sprintf("%v:%v", ip, port)
	ip := net.ParseIP(self.host)
	self.fd, err = net.ListenTCP("tcp", &(net.TCPAddr{ip, self.port}))
	if err != nil {
		self.Done()
		return
	}

	//listener accpet thread
	self.Success("listen thread num:%d ", self.accept_thread_num)
	var i int
	for i = 1; i <= self.accept_thread_num; i++ {
		go func(id int) {
			var err error = nil
			var fd *net.TCPConn
			self.Add(1)
			self.Success("listen thread:%d work!", id)

			for /*err == nil*/ {
				fd, err = self.fd.AcceptTCP()
				acc := <-self.chan_free_acc //acquire acceptor from pool
				if err == nil {
					fd.SetReadBuffer(self.recv_buf_size)
					fd.SetWriteBuffer(self.send_buf_size)
					acc.startup(fd)
					self.CallMethod("listen-conn-made", id, acc)
				}
			}

			self.Error("listen thread:%d free!", id)
			self.Done()
		}(i)
	} // end: for

	return
}

func (self *TcpListener) GetName() string {
	return self.name
}

func (self *TcpListener) get_log() Log {
	return self.log
}

func (self *TcpListener) get_rcvbuf_size() int {
	return self.recv_buf_size
}

func (self *TcpListener) get_sndbuf_size() int {
	return self.send_buf_size
}

func (self *TcpListener) get_sndchan_num() int {
	return self.send_chan_num
}

func (self *TcpListener) get_max_idle() int64 {
	return self.local_max_idle_sec
}
